from ctypes import *
from ..zlgcan.zlgcan import *
from .zuds_structure import *
import platform
import threading
import time
from tkinter import messagebox


class PARAM_DATA(Structure):  # 用于存放uds的参数内容
    _pack_ = 1
    _fields_ = [("data", c_ubyte*4096)]


@WINFUNCTYPE(c_uint32, c_void_p, POINTER(ZUDS_FRAME), c_uint32)  # 设置发送回调实例
def transmit_callback(ctx, frame, count):
    # print("frame num is:%d"%count)
    zcan_data = (ZCAN_Transmit_Data * 500)()
    memset(zcan_data, 0, sizeof(zcan_data))  # 确保队列发送中的"帧间隔为0"
    for i in range(count):
        zcan_data[i].frame.can_id = frame[i].id
        if Zuds_Tester_Base.Extend_Flag:
            zcan_data[i].frame.eff = 1
        zcan_data[i].frame.can_dlc = 8  # frame[i].data_len
        # zcan_data[i].frame.__pad =0x20  #设置发送回显
        for j in range(frame[i].data_len):
            zcan_data[i].frame.data[j] = frame[i].data[j]
    # ctx传通道句柄的情况下，演示CAN报文，如想发CANFD报文，可自行设置全局flag，调用TransmitFD
    result = Zuds_Tester_Base.zcanlib.Transmit(ctx, zcan_data, count)
    if result == count:
        return 0
    else:
        return 1


class ZUDS(object):
    def __init__(self):
        if platform.system() == "Windows":
            self.__dll = windll.LoadLibrary("./lib_dlls/zuds.dll")
        else:
            print("No support now!")
        if self.__dll == None:
            print("DLL couldn't be loaded!")

    def Uds_Init(self, type_):
        try:
            return self.__dll.ZUDS_Init(type_)
        except:
            print("Excetion on Udsinit")

    def Uds_SetTransmitHandler(self, zuds_handle, ctx):
        try:
            return self.__dll.ZUDS_SetTransmitHandler(zuds_handle, ctx, transmit_callback)
        except:
            print("Exception on SetTransmitHandler")

    def Uds_Request(self, handle, request, response):
        try:
            return self.__dll.ZUDS_Request(handle, byref(request), byref(response))
        except:
            print("Exception on UDSRequest")

    def Uds_Onreceive(self, handle, uds_frame):
        try:
            return self.__dll.ZUDS_OnReceive(handle, byref(uds_frame))
        except:
            print("Exception on uds_Onreceive")

    # type =0设置ZUDS_SESSION_PARAM，type=1设置ZUDS_ISO15765_PARAM
    def Uds_SetParam(self, handle, type_, param):
        try:
            return self.__dll.ZUDS_SetParam(handle, type_, byref(param))
        except:
            print("Exception on uds_SetParam")

    def Uds_SetTesterPresent(self, handle, enable, param):
        try:
            return self._dll.ZUDS_SetTesterPresent(handle, enable, byref(param))
        except:
            print("Exception on SetTesterPresent")

    def Uds_Release(self, handle):
        try:
            return self.__dll.ZUDS_Release(handle)
        except:
            print("Exception on uds_Release")

    def Uds_Stop(self, handle):
        try:
            return self.__dll.ZUDS_Stop(handle)
        except:
            print("Exception on UDS_STOP")


GLOBAL_ZUDSLIB = ZUDS()


class Zuds_Tester_Base:
    # 这几个变量会在全局中使用，特别是回调函数中用到，所以放在类中，让外部通过类就可以访问，而不需要等待实例创建后才能访问
    # zcanlib = ZCAN()
    zcanlib = GLOBAL_ZCANLIB
    Extend_Flag = 0  # 0-标准帧 ，1-扩展帧

    # DOC: 在类中直接打开zcanlib设备,后面所有的实例可以共用该设备句柄，只是选择不同的通道
    # 避免了每次实例化都要打开设备的操作
    if 1:
        device_handle = zcanlib.OpenDevice(
            ZLG_CONSTANT.ZCAN_USBCANFD_400U, 0, 0)  # 打开设备
        if device_handle == ZLG_CONSTANT.INVALID_DEVICE_HANDLE:
            print("Open CANFD Device failed!")
            # exit(0)
            messagebox.showerror("错误", "设备未正常打开，程序将无法正常工作...")

        else:
            print("Open CANFD Device success!")
            print("device handle:%d." % (device_handle))
        info = zcanlib.GetDeviceInf(device_handle)  # 打印设备信息
        print("Device Information:\n%s" % (info))

    def __init__(self, funcjob=None, channel=0) -> None:
        self.zudslib = GLOBAL_ZUDSLIB
        self.START_RECIEIVE_FLAG = 0

        self.FUNCJOB = funcjob

        self.channel = channel

        # 启动通道,获取通道句柄
        self.chn_handle = self.canfd_start()
        print("chn_handle:", hex(self.chn_handle))

        # 初始化UDS，获取UDS句柄
        self.uds_handle = self.zudslib.Uds_Init(0)  # 初始化UDS，获取UDS句柄
        print("uds_handle is %d" % self.uds_handle)

        # 设置发送回调
        self.set_transmit_handler()

        # 设置UDS参数
        self.Set_Uds_param()

    def set_Base_param(self, IProperty):

        ret = self.zcanlib.SetValue(IProperty, str(self.channel) +
                                    "/initenal_resistance", "1")
        if ret != ZLG_CONSTANT.ZCAN_STATUS_OK:
            print("Open CH%d resistance failed!" % (self.channel))
        ret = self.zcanlib.SetValue(
            # 设置波特率
            IProperty, str(self.channel)+"/canfd_abit_baud_rate", "500000")
        ret = self.zcanlib.SetValue(IProperty, str(self.channel) +
                                    "/canfd_dbit_baud_rate", "2000000")
        if ret != ZLG_CONSTANT.ZCAN_STATUS_OK:
            print("Set CH%d baud failed!" % (self.channel))

        ret = self.zcanlib.SetValue(IProperty, "0/set_cn", "A001")
        if ret == ZLG_CONSTANT.ZCAN_STATUS_OK:
            t = self.zcanlib.GetValue(IProperty, "0/get_cn/1")
            print(t)

    def canfd_start(self):
        # 获取IProperty
        ip = self.zcanlib.GetIProperty(self.device_handle)

        # 设置基础参数
        self.set_Base_param(IProperty=ip)

        # 初始化CAN
        _chn_handle = self.initCan()
        if _chn_handle is None:
            return None

        # 设置滤波
        self.set_Filter(ip)

        # 启动CAN
        self.zcanlib.StartCAN(_chn_handle)

        # 释放IProperty
        self.zcanlib.ReleaseIProperty(ip)
        return _chn_handle

    def set_Other_param(self, IProperty):
        # 设置是否启动队列发送模式，需要在startcan之后设置
        # 选择是否启动队列发送模式，0-不启动，1-启动
        ret = self.zcanlib.SetValue(IProperty, str(self.channel)+"/set_send_mode",
                                    "0")
        if ret == ZLG_CONSTANT.ZCAN_STATUS_OK:
            print("set_queue_send_mode success!")
        else:
            print("set queue fail")
        # 设置是否启动回显模式
        ret = self.zcanlib.SetValue(
            IProperty, str(self.channel)+"set_device_tx_echo", "1")

    def set_Filter(self, ip):
        from collections import namedtuple
        Filter_Section = namedtuple("Filter_Section", ["start", "end"])

        # 标准帧滤波功能函数
        def std_Frame_filter(filterSections):
            for section in filterSections:
                ret = self.zcanlib.SetValue(ip, str(self.channel) +
                                            "/filter_mode", "0")  # 标准帧滤波
                if ret != ZLG_CONSTANT.ZCAN_STATUS_OK:
                    print("Set CH%d USBCANFD filter_mode failed!" %
                          (self.channel))
                ret = self.zcanlib.SetValue(
                    ip, str(self.channel)+"/filter_start", section.start)
                if ret != ZLG_CONSTANT.ZCAN_STATUS_OK:
                    print("Set CH%d USBCANFD filter_start failed!" %
                          (self.channel))
                ret = self.zcanlib.SetValue(
                    ip, str(self.channel)+"/filter_end", section.end)
                if ret != ZLG_CONSTANT.ZCAN_STATUS_OK:
                    print("Set CH%d USBCANFD filter_end failed!" %
                          (self.channel))

        # 扩展帧滤波功能函数
        def fd_Frame_filter():
            ret = self.zcanlib.SetValue(ip, str(self.channel) +
                                        "/filter_mode", "1")
            if ret != ZLG_CONSTANT.ZCAN_STATUS_OK:
                print("Set CH%d USBCANFD filter_mode failed!" % (self.channel))
            ret = self.zcanlib.SetValue(
                ip, str(self.channel)+"/filter_start", "0")
            if ret != ZLG_CONSTANT.ZCAN_STATUS_OK:
                print("Set CH%d USBCANFD filter_start failed!" %
                      (self.channel))
            ret = self.zcanlib.SetValue(ip, str(self.channel) +
                                        "/filter_end", "0x1FFFFFFF")
            if ret != ZLG_CONSTANT.ZCAN_STATUS_OK:
                print("Set CH%d USBCANFD filter_end failed!" % (self.channel))

        # SET filter
        # 清除滤波设置
        ret = self.zcanlib.SetValue(ip, str(self.channel)+"/filter_clear", "0")
        if ret != ZLG_CONSTANT.ZCAN_STATUS_OK:
            print("Set CH%d USBCANFD filter_clear failed!" % (self.channel))

        # 标准帧滤波设置
        filterSections_all = [Filter_Section("0x0", "0x7ff")]
        filterSections_set = [Filter_Section(
            "0x7a4", "0x7a6"), Filter_Section("0x7b4", "0x7b8")]

        std_Frame_filter(filterSections_all)

        # 扩展帧滤波设置
        fd_Frame_filter()

        # 过滤使能生效
        ret = self.zcanlib.SetValue(ip, str(self.channel)+"/filter_ack", "0")
        if ret != ZLG_CONSTANT.ZCAN_STATUS_OK:
            print("Set CH%d USBCANFD filter_ack failed!" % (self.channel))

    def Set_Uds_param(self):
        param_15765 = ZUDS_ISO15765_PARAM()
        memset(byref(param_15765), 0, sizeof(param_15765))
        param_15765.version = 0
        param_15765.max_data_len = 8
        param_15765.local_st_min = 0
        param_15765.block_size = 8
        param_15765.fill_byte = 0
        param_15765.frame_type = self.Extend_Flag  # 0-标准帧，1-扩展帧
        param_15765.is_modify_ecu_st_min = 0  # 是否修改 ECU 的最小发送时间间隔参数
        param_15765.remote_st_min = 0
        param_15765.fc_timeout = 70  # 等待流控超时时间，单位ms
        param_15765.fill_mode = 1  # 数据长度填充模式，0-不填充，1-小于8字节填充到8，大于8字节就近填充，2-填充至最大字节
        self.zudslib.Uds_SetParam(self.uds_handle, 1, param_15765)

        param_seesion = ZUDS_SESSION_PARAM()
        memset(byref(param_seesion), 0, sizeof(param_seesion))
        param_seesion.timeout = 2000
        param_seesion.enhanced_timeout = 5000
        self.zudslib.Uds_SetParam(self.uds_handle, 0, param_seesion)
        print("set_param completed")

    def Read_Thread_Func(self):
        uds_frame = ZUDS_FRAME()
        # print("START_RECIEIVE_FLAG", self.START_RECIEIVE_FLAG)
        while self.START_RECIEIVE_FLAG:

            rcv_num = self.zcanlib.GetReceiveNum(
                self.chn_handle, ZLG_CONSTANT.ZCAN_TYPE_CAN)
            # print(f"[file:{__file__}]=>[line:{(sys._getframe().f_lineno)
            #                                   }]=======rcv_num:{rcv_num}")

            rcv_canfd_num = self.zcanlib.GetReceiveNum(
                self.chn_handle, ZLG_CONSTANT.ZCAN_TYPE_CANFD)
            if rcv_num:
                rcv_msg, rcv_num = self.zcanlib.Receive(
                    self.chn_handle, rcv_num, 1)
                for i in range(rcv_num):
                    memset(byref(uds_frame), 0, sizeof(uds_frame))
                    # 传入真实ID
                    uds_frame.id = rcv_msg[i].frame.can_id & 0x1fffffff
                    uds_frame.extend = rcv_msg[i].frame.eff
                    uds_frame.data_len = rcv_msg[i].frame.can_dlc
                    for j in range(uds_frame.data_len):
                        uds_frame.data[j] = rcv_msg[i].frame.data[j]

                    '''
                    print("[{i}]:timestamps:{timestamps},type:{rx_tx}, ,id:{id}, dlc:{dlc}, eff:{eff}, rtr:{rtr}, data:{data}".format(i=i,
                                                                                                                                      timestamps=rcv_msg[
                                                                                                                                          i].timestamp,
                                                                                                                                      rx_tx=(
                                                                                                                                          "tx" if (getattr(rcv_msg[i].frame, "__pad") & 0x20) else "rx"),
                                                                                                                                      id=hex(
                                                                                                                                          rcv_msg[i].frame.can_id),
                                                                                                                                      dlc=rcv_msg[
                                                                                                                                          i].frame.can_dlc,
                                                                                                                                      eff=rcv_msg[
                                                                                                                                          i].frame.eff,
                                                                                                                                      rtr=rcv_msg[
                                                                                                                                          i].frame.rtr,
                                                                                                                                      data=''.join(hex(rcv_msg[i].frame.data[j]) + ' 'for j in range(rcv_msg[i].frame.can_dlc))))

                    '''

                    self.zudslib.Uds_Onreceive(self.uds_handle, uds_frame)

            if rcv_canfd_num:
                rcv_canfd_msgs, rcv_canfd_num = self.zcanlib.ReceiveFD(
                    self.chn_handle, rcv_canfd_num, 1)
                for i in range(rcv_canfd_num):
                    memset(byref(uds_frame), 0, sizeof(uds_frame))
                    # 传入真实ID
                    uds_frame.id = rcv_canfd_msgs[i].frame.can_id & 0x1fffffff
                    uds_frame.extend = rcv_canfd_msgs[i].frame.eff
                    uds_frame.data_len = rcv_canfd_msgs[i].frame.len
                    for j in range(uds_frame.data_len):
                        uds_frame.data[j] = rcv_canfd_msgs[i].frame.data[j]
                    print("[%d]:timestamp:%d,type:canfd, id:%s, len:%d, eff:%d, rtr:%d, esi:%d, brs: %d, data:%s" % (
                        i, rcv_canfd_msgs[i].timestamp, hex(
                            rcv_canfd_msgs[i].frame.can_id), rcv_canfd_msgs[i].frame.len,
                        rcv_canfd_msgs[i].frame.eff, rcv_canfd_msgs[i].frame.rtr,
                        rcv_canfd_msgs[i].frame.esi, rcv_canfd_msgs[i].frame.brs,
                        ''.join(hex(rcv_canfd_msgs[i].frame.data[j]) + ' ' for j in range(rcv_canfd_msgs[i].frame.len))))
                    self.zudslib.Uds_Onreceive(self.uds_handle, uds_frame)
            time.sleep(0.01)
        # print("exit receive")

    def set_transmit_handler(self):
        ret = self.zudslib.Uds_SetTransmitHandler(
            self.uds_handle, self.chn_handle)  # 设置发送回调,将uds句柄与通道句柄绑定
        if ret == 0:
            print("Uds_SetTransmitHandler success")
        else:
            print("Uds_SetTransmitHandler fail")

    def initCan(self):
        chn_init_cfg = ZCAN_CHANNEL_INIT_CONFIG()
        chn_init_cfg.can_type = ZLG_CONSTANT.ZCAN_TYPE_CANFD
        # mode为0表示正常模式，同时可以收发数据，为1表示只听模式，只能接收数据
        chn_init_cfg.config.canfd.mode = 0
        return self.zcanlib.InitCAN(
            self.device_handle, self.channel, chn_init_cfg)

    def resetCan(self):
        # Close CAN
        ret = self.zcanlib.ResetCAN(self.chn_handle)
        if ret == 1:
            print("ResetCAN success! ")

    def close_Device(self):
        # Close Device
        ret = self.zcanlib.CloseDevice(self.device_handle)
        if ret == 1:
            print("CloseDevice success! ")

    def uds_send_operate(self):
        # DOC 原计划 让子类实现，父类不能直接实例化
        # 实际上父类还是可以实例化，只有不调用这个函数就可以了
        raise Exception("Not implemented")

    def handle_uds_msg(self, req_src_addr=0x737, req_dst_addr=0x7b7, req_sid=0x22, req_param_len=0x2, req_data_1=[0xf1, 0x90], *args, **kwargs):
        '''
        处理发送read_vin的请求
        '''

        # print(f'[file:{__file__}]=>[line:{(sys._getframe().f_lineno)}]==> handle_uds_msg:{
        #   req_src_addr} {req_dst_addr} {req_sid} {req_param_len} {req_data_1}')
        # 设定Read_Thread_Func的循环标志，开始接收UDS数据
        self.START_RECIEIVE_FLAG = 1
        self.read_thread_ = threading.Thread(
            None, target=self.Read_Thread_Func, args=())
        self.read_thread_.start()

        # print("request ")
        request = ZUDS_REQUEST()
        memset(byref(request), 0, sizeof(request))
        request.src_addr = req_src_addr
        request.dst_addr = req_dst_addr
        request.sid = req_sid
        request.param_len = req_param_len  # 参数长度
        data_1 = req_data_1
        param_data = PARAM_DATA()  # 参数的list，成员数量需与request.param_len对应
        for i in range(request.param_len):
            param_data.data[i] = data_1[i]
        request.param = param_data.data
        response = ZUDS_RESPONSE()

        print("request sid:", hex(request.sid), "request param_len:", req_param_len, "req_src_addr:", hex(req_src_addr),
              "req_dst_addr:", hex(req_dst_addr), "req_data_1:", ''.join(hex(req_data_1[i])+' ' for i in range(req_param_len)))

        self.zudslib.Uds_Request(
            self.uds_handle, request, response)
        memset(byref(param_data), 0, sizeof(param_data))

        # 处理响应
        # 等待一段时间，等待接收线程接收到数据
        # time.sleep(0.3)

        # 设定Read_Thread_Func的循环标志，停止接收UDS数据
        self.START_RECIEIVE_FLAG = 0
        self.read_thread_.join()
        self.zudslib.Uds_Stop(self.uds_handle)

        # print(f'[file:{__file__}]=>[line:{
        #   (sys._getframe().f_lineno)}]==>before_handle_uds_msg return ....')
        return response

    def uds_send_operate_example(self):
        # 设置发送回调，移到初始化中
        # self.set_transmit_handler()

        # 设置UDS参数 ，移到初始化中
        # self.Set_Uds_param()

        # 设定Read_Thread_Func的循环标志，开始接收UDS数据
        self.START_RECIEIVE_FLAG = 1
        self.read_thread_ = threading.Thread(
            None, target=self.Read_Thread_Func, args=())
        self.read_thread_.start()

        # print("request ")
        request = ZUDS_REQUEST()
        memset(byref(request), 0, sizeof(request))
        request.src_addr = 0x737
        request.dst_addr = 0x7b7
        request.sid = 0x22
        request.param_len = 0x2  # 参数长度
        param_data = PARAM_DATA()  # 参数的list，成员数量需与request.param_len对应
        data_1 = [0xf1, 0x90]
        for i in range(request.param_len):
            param_data.data[i] = data_1[i]
        request.param = param_data.data
        response = ZUDS_RESPONSE()

        # BUG: 通道为0可以正常工作，其他通道不可以，问题在此处
        self.zudslib.Uds_Request(self.uds_handle, request, response)
        memset(byref(param_data), 0, sizeof(param_data))

        # if response.status == 0:
        #     if response.type == 0:
        #         print("消极响应：%s 服务号： %s  消极码： %s" % (hex(response.response.negative.neg_code), hex(
        #             response.response.negative.sid), hex(response.response.negative.error_code)))
        #     if response.type == 1:
        #         print("积极响应,响应id:%s,参数长度:%d,参数内容：%s" % (hex(response.response.positive.sid), response.response.positive.param_len, ''.join(
        #             hex(response.response.positive.param[i])+' 'for i in range(response.response.positive.param_len))))
        # if response.status == 1:
        #     print("响应超时")
        # if response.status == 2:
        #     print("传输失败，请检查链路层，或请确认流控帧是否回复")
        # if response.status == 3:
        #     print("取消请求")
        # if response.status == 4:
        #     print("抑制响应")
        # if response.status == 5:
        #     print("忙碌中")
        # if response.status == 6:
        print("请求参数错误")

        # time.sleep(0.3)

        # 设定Read_Thread_Func的循环标志，停止接收UDS数据
        self.START_RECIEIVE_FLAG = 0
        self.read_thread_.join()
        self.zudslib.Uds_Stop(self.uds_handle)


GLOBAL_ZUDS_TESTER = Zuds_Tester_Base()

if __name__ == "__main__":
    pass
